<template>
  <div class="app-container">
    <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="病症" prop="disease">
        <el-input
            v-model="queryParams.disease"
            placeholder="请输入病症名称"
            clearable
            @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item label="特征" prop="characteristics">
        <el-input
            v-model="queryParams.characteristics"
            placeholder="请输入特征名称"
            clearable
            @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item label="问题" prop="problem">
        <el-input
            v-model="queryParams.problem"
            placeholder="请输入问题"
            clearable
            @keyup.enter="handleQuery"
        />
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
        <el-button icon="Refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button
            type="primary"
            plain
            icon="Plus"
            @click="handleAdd"
        >新建关系
        </el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
            type="danger"
            plain
            icon="Delete"
            @click="handleDelete"
        >删除关系
        </el-button>
      </el-col>
    </el-row>
    <div ref="chartContainer"></div>
  </div>
</template>

<script setup>
import {ref, onMounted} from 'vue';
import * as d3 from 'd3';
import {listNodeAndEdge, makeReleation, delReleation} from "../../../api/medical/releation/releation.js";

const {proxy} = getCurrentInstance();
const open = ref(false);
const loading = ref(true);
const showSearch = ref(true);

// 假设你有一些模拟数据
const nodes = ref([]);

const links = ref([]);

const chartContainer = ref(null);
var color = d3.schemeCategory10;

var colorMap = {
  1: 'blue',     //医学病症
  2: 'lightgreen',      //医学特征
  3: 'green',    //医学问题
  4: 'lightgray' //医学答案
};

const data = reactive({
  form: {},
  queryParams: {
    disease: null,
    characteristics: null,
    problem: null
  },
  rules: {}
});

const {queryParams, form, rules} = toRefs(data);

/** 搜索按钮操作 */
function handleQuery() {
  getNodeAndEdgeList();
}


/**
 * 查询node和关系
 */
function getNodeAndEdgeList() {
  loading.value = true;
  nodes.value = [];
  links.value = [];
  listNodeAndEdge(queryParams.value).then(response => {
    for (var i = 0; i < response.data.nodeList.length; i++) {
      let node = {};
      node.id = response.data.nodeList[i].id;
      node.type = response.data.nodeList[i].type;
      node.name = response.data.nodeList[i].name;
      node.selected = response.data.nodeList[i].selected;
      nodes.value.push(node);
    }
    for (var i = 0; i < response.data.edgeList.length; i++) {
      let link = {};
      link.source = response.data.edgeList[i].source;
      link.target = response.data.edgeList[i].target;
      link.relation = response.data.edgeList[i].relation;
      links.value.push(link);
    }
    // console.log("node:" + nodes.value + ";link:" + links.value);
    loading.value = false;
    initSvg();
  });
}

/**
 * 新建关系
 */
function handleAdd() {
  //获取选中的节点，如果不等于2则进行提示
  // 使用数组的filter方法筛选出selected为true的节点
  var selectedNodes = nodes.value.filter(function (node) {
    return node.selected === true;
  });
  if (selectedNodes.length != 2) {
    proxy.$modal.msgError("建立关系只能选两个节点！");
    return;
  }
  //判断是否符合创建关系  病症 -> 特征    特征  ->  问题答案       问题答案   ->    问题     病症：
  //后端校验
  form.value.source = selectedNodes[0].id;
  form.value.target = selectedNodes[1].id;
  makeReleation(form.value).then(response => {
    proxy.$modal.msgSuccess("新建关系成功");
    //触发重新查询
    handleQuery();
  });
}

/**
 * 删除关系
 */
function handleDelete() {
  var selectedNodes = nodes.value.filter(function (node) {
    return node.selected === true;
  });
  if (selectedNodes.length != 2) {
    proxy.$modal.msgError("删除关系只能选两个节点！");
    return;
  }
  //判断是否符合删除关系  病症 -> 特征    特征  ->  问题答案       问题答案   ->    问题     病症：
  //后端校验
  form.value.source = selectedNodes[0].id;
  form.value.target = selectedNodes[1].id;
  delReleation(form.value).then(response => {
    proxy.$modal.msgSuccess("删除关系成功");
    //触发重新查询
    handleQuery();
  });
}

/** 重置按钮操作 */
function resetQuery() {
  proxy.resetForm("queryRef");
  handleQuery();
}

// 初始化图表
onMounted(() => {

  getNodeAndEdgeList();


});

function initSvg() {
  var w = 1800;
  var h = 700;
  //重置svg
  d3.select(chartContainer.value).selectAll("*").remove();


  //声明svg元素，指定样式和宽度和高度
  var svg = d3.select(chartContainer.value)
      .append("svg")
      .attr("width", w)
      .attr("height", h)
      .style("position", "absolute") // 如果需要相对于某个元素定位，可以添加这个样式
      .style("right", 20 + "px"); // 设置SVG的左偏移量

  // 给 SVG 添加边框样式
  svg.style("border", "1px solid black");

  // 创建一个zoom行为
  var zoom = d3.zoom()
      .scaleExtent([0.1, 10]) // 设置缩放的范围
      .on("zoom", zoomed)
      .on("start", function(event) {
        event.sourceEvent.stopPropagation();
      });;

  // 应用zoom行为到SVG元素
  svg.call(zoom);

  // 监听滚轮事件,实现缩放功能
  svg.on("wheel", function(event) {
    event.preventDefault();
    var delta = event.deltaY * -0.01;
    var transform = d3.zoomTransform(this);
    var newScale = transform.k * Math.exp(delta);

    if (newScale > 10) newScale = 10;
    if (newScale < 0.1) newScale = 0.1;

    svg.transition()
        .duration(100)
        .call(zoom.transform, d3.zoomIdentity.translate(transform.x, transform.y).scale(newScale));
  });

  // 定义zoomed函数来处理缩放事件
  function zoomed(event) {
    var transform = d3.zoomTransform(svg.node());

    // 更新需要缩放的图形元素
    // circle.attr("transform", transform);
    // rectangle.attr("transform", transform);
    svg.attr("transform", event.transform);
  }

  // 创建模拟
  const simulation = d3.forceSimulation()
      .force("link", d3.forceLink(links).id(function (d) {
        return d.id;
      }))
      .force("charge", d3.forceManyBody().strength(-10))//引力
      .force("center", d3.forceCenter(w / 2, h / 2))
      .force('collision', d3.forceCollide().radius(50).iterations(2));


  // 假设你有一个模拟实例叫做 simulation
  simulation.alphaTarget(0).restart(); // 停止当前模拟
  simulation.alphaTarget(0.3).restart(); // 开始新的模拟

  //
  simulation.nodes(nodes.value)
      .on("tick");

  simulation.force("link")
      .links(links.value)
      .distance(100);

  var link = svg.selectAll(".link")
      .data(links.value)
      .enter()
      .append("line")
      .attr("class", "link")
      .style("stroke-width", 1)
      .style("stroke", (d, i) => color[i % 10])
      .style("opacity", 0.6);

  var node = svg.selectAll(".node")
      .data(nodes.value)
      .enter()
      .append("circle")
      .attr("r", 12)
      .attr("fill", function (d) {
        return colorMap[d.type];
      })
      .call(drag())
      .on('click', function (d, nde) {
        // 切换选中状态
        nde.selected = !nde.selected;
        var isselected = nde.selected;
        // 更新节点颜色以反映选中状态
        d3.select(this)
            .transition() // 使用过渡使颜色变化更平滑
            .attr('fill', isselected ? 'red' : colorMap[nde.type]);
      });

  simulation.on("tick", () => {
    link.attr("x1", d => d.source.x)
        .attr("y1", d => d.source.y)
        .attr("x2", d => d.target.x)
        .attr("y2", d => d.target.y);

    node.each(function (d) {
      // 检查节点是否超出 SVG 边界
      if (d.x < 0) d.x = 0 + 0; // 如果 x 小于 0，则将其设置为 0
      if (d.y < 0) d.y = 0 + 0; // 如果 y 小于 0，则将其设置为 0
      if (d.x > w) d.x = w - 0; // 如果 x 大于 SVG 宽度，则将其设置为 SVG 宽度
      if (d.y > h) d.y = h - 0; // 如果 y 大于 SVG 高度，则将其设置为 SVG 高度
      // 更新节点的位置
      d3.select(this)
          .attr("cx", d => d.x)
          .attr("cy", d => d.y);
    });

    edges_text
        .attr("x", d => (d.source.x + d.target.x) / 2)
        .attr("y", d => (d.source.y + d.target.y) / 2);

    texts
        .attr("x", d => d.x)
        .attr("y", d => d.y);
  });

  //拖拽
  function drag() {
    function dragstarted(event, d) {
      if (!event.active) simulation.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }

    function dragged(event, d) {
      d.fx = event.x;
      d.fy = event.y;
    }

    function dragended(event, d) {
      if (!event.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }

    return d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended);
  };
  var edges_text = svg.selectAll(".linetext")
      .data(links.value)
      .enter()
      .append("text")
      .attr("class", "linetext")
      .text(d => d.relation)
      .style("stroke", (d, i) => color[i % 10])
      .style("font-size", 10);

  //渲染节点
  var texts = svg.selectAll(".forceText")
      .data(nodes.value)
      .enter()
      .append("text")
      .attr("class", "forceText")
      .style("stroke", (d, i) => color[i % 10])
      .style("font-size", "10px")
      .attr("text-anchor", "middle")
      .attr("dy", 50)
      .text(d => d.name);
}


</script>

<style scoped>
.links line {
  stroke: #999;
  stroke-opacity: 0.6;
}

.nodes circle {
  stroke: #fff;
  stroke-width: 1.5px;
}
</style>